package com.acme.superlogger.slf4j;

import org.slf4j.Logger;
import org.slf4j.Marker;

import java.util.Date;

public class SuperLogger implements Logger {


    private Level threshold;

    private final String logFormat;

    private final AsyncWriter writer;

    private final String name;

    public SuperLogger(String format, AsyncWriter out, String name, Level threshold) {
        this.logFormat = format;
        this.writer = out;
        this.name = name;
        this.threshold = threshold;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public boolean isTraceEnabled() {
        return threshold.includes(Level.TRACE);
    }

    @Override
    public void trace(String msg) {
        log(Level.TRACE, msg);
    }

    @Override
    public void trace(String format, Object arg) {
        log(Level.TRACE, format, arg);
    }

    @Override
    public void trace(String format, Object arg1, Object arg2) {
        log(Level.TRACE, format, arg1, arg2);
    }

    @Override
    public void trace(String format, Object... arguments) {
        log(Level.TRACE, format, arguments);
    }

    @Override
    public void trace(String msg, Throwable t) {
        log(Level.TRACE, msg + t.toString());
    }

    @Override
    public boolean isTraceEnabled(Marker marker) {
        return threshold.includes(Level.TRACE);
    }

    @Override
    public void trace(Marker marker, String msg) {
        log(Level.TRACE, msg);
    }

    @Override
    public void trace(Marker marker, String format, Object arg) {
        log(Level.TRACE, format, arg);
    }

    @Override
    public void trace(Marker marker, String format, Object arg1, Object arg2) {
        log(Level.TRACE, format, arg1, arg2);
    }

    @Override
    public void trace(Marker marker, String format, Object... argArray) {
        log(Level.TRACE, format, argArray);
    }

    @Override
    public void trace(Marker marker, String msg, Throwable t) {
        log(Level.TRACE, msg + t.toString());
    }


    // DEBUG
    @Override
    public boolean isDebugEnabled() {
        return threshold.includes(Level.DEBUG);
    }

    @Override
    public void debug(String msg) {
        log(Level.DEBUG, msg);
    }

    @Override
    public void debug(String format, Object arg) {
        log(Level.DEBUG, format, arg);
    }

    @Override
    public void debug(String format, Object arg1, Object arg2) {
        log(Level.DEBUG, format, arg1, arg2);
    }

    @Override
    public void debug(String format, Object... arguments) {
        log(Level.DEBUG, format, arguments);
    }

    @Override
    public void debug(String msg, Throwable t) {
        log(Level.DEBUG, msg + t.toString());
    }

    @Override
    public boolean isDebugEnabled(Marker marker) {
        return threshold.includes(Level.DEBUG);
    }

    @Override
    public void debug(Marker marker, String msg) {
        log(Level.DEBUG, msg);
    }

    @Override
    public void debug(Marker marker, String format, Object arg) {
        log(Level.DEBUG, format, arg);
    }

    @Override
    public void debug(Marker marker, String format, Object arg1, Object arg2) {
        log(Level.DEBUG, format, arg1, arg2);
    }

    @Override
    public void debug(Marker marker, String format, Object... argArray) {
        log(Level.DEBUG, format, argArray);
    }

    @Override
    public void debug(Marker marker, String msg, Throwable t) {
        log(Level.DEBUG, msg + t.toString());
    }

    //INFO
    @Override
    public boolean isInfoEnabled() {
        return threshold.includes(Level.INFO);
    }

    @Override
    public void info(String msg) {
        log(Level.INFO, msg);
    }

    @Override
    public void info(String format, Object arg) {
        log(Level.INFO, format, arg);
    }

    @Override
    public void info(String format, Object arg1, Object arg2) {
        log(Level.INFO, format, arg1, arg2);
    }

    @Override
    public void info(String format, Object... arguments) {
        log(Level.INFO, format, arguments);
    }

    @Override
    public void info(String msg, Throwable t) {
        log(Level.INFO, msg + t.toString());
    }

    @Override
    public boolean isInfoEnabled(Marker marker) {
        return threshold.includes(Level.INFO);
    }

    @Override
    public void info(Marker marker, String msg) {
        log(Level.INFO, msg);
    }

    @Override
    public void info(Marker marker, String format, Object arg) {
        log(Level.INFO, format, arg);
    }

    @Override
    public void info(Marker marker, String format, Object arg1, Object arg2) {
        log(Level.INFO, format, arg1, arg2);
    }

    @Override
    public void info(Marker marker, String format, Object... argArray) {
        log(Level.INFO, format, argArray);
    }

    @Override
    public void info(Marker marker, String msg, Throwable t) {
        log(Level.INFO, msg + t.toString());
    }

    //WARN

    @Override
    public boolean isWarnEnabled() {
        return threshold.includes(Level.WARN);
    }

    @Override
    public void warn(String msg) {
        log(Level.WARN, msg);
    }

    @Override
    public void warn(String format, Object arg) {
        log(Level.WARN, format, arg);
    }

    @Override
    public void warn(String format, Object arg1, Object arg2) {
        log(Level.WARN, format, arg1, arg2);
    }

    @Override
    public void warn(String format, Object... arguments) {
        log(Level.WARN, format, arguments);
    }

    @Override
    public void warn(String msg, Throwable t) {
        log(Level.WARN, msg + t.toString());
    }

    @Override
    public boolean isWarnEnabled(Marker marker) {
        return threshold.includes(Level.WARN);
    }

    @Override
    public void warn(Marker marker, String msg) {
        log(Level.WARN, msg);
    }

    @Override
    public void warn(Marker marker, String format, Object arg) {
        log(Level.WARN, format, arg);
    }

    @Override
    public void warn(Marker marker, String format, Object arg1, Object arg2) {
        log(Level.WARN, format, arg1, arg2);
    }

    @Override
    public void warn(Marker marker, String format, Object... argArray) {
        log(Level.WARN, format, argArray);
    }

    @Override
    public void warn(Marker marker, String msg, Throwable t) {
        log(Level.WARN, msg + t.toString());
    }

    //ERROR
    @Override
    public boolean isErrorEnabled() {
        return threshold.includes(Level.ERROR);
    }

    @Override
    public void error(String msg) {
        log(Level.ERROR, msg);
    }

    @Override
    public void error(String format, Object arg) {
        log(Level.ERROR, format, arg);
    }

    @Override
    public void error(String format, Object arg1, Object arg2) {
        log(Level.ERROR, format, arg1, arg2);
    }

    @Override
    public void error(String format, Object... arguments) {
        log(Level.ERROR, format, arguments);
    }

    @Override
    public void error(String msg, Throwable t) {
        log(Level.ERROR, msg + t.toString());
    }

    @Override
    public boolean isErrorEnabled(Marker marker) {
        return threshold.includes(Level.ERROR);
    }

    @Override
    public void error(Marker marker, String msg) {
        log(Level.ERROR, msg);
    }

    @Override
    public void error(Marker marker, String format, Object arg) {
        log(Level.ERROR, format, arg);
    }

    @Override
    public void error(Marker marker, String format, Object arg1, Object arg2) {
        log(Level.ERROR, format, arg1, arg2);
    }

    @Override
    public void error(Marker marker, String format, Object... argArray) {
        log(Level.ERROR, format, argArray);
    }

    @Override
    public void error(Marker marker, String msg, Throwable t) {
        log(Level.ERROR, msg + t.toString());
    }

    private void log(Level level, String format, Object... args) {
        if (threshold.includes(level)) {
            writer.putLine(generateString(level, format, args));
        }
    }

    private String generateString(Level level, String format, Object... args) {
        String result = logFormat;
        if (result.contains("%d")) {
            result = result.replaceFirst("%d", new Date().toString());
        }
        if (result.contains("%n")) {
            result = result.replaceFirst("%n", name);
        }
        if (result.contains("%l")) {
            result = result.replaceFirst("%l", level.name());
        }
        if (result.contains("%m")) {
            result = result.replaceFirst("%m", MessageFormatter.format(format, args));
        }
        return result;
    }

}
